#!/usr/bin/perl

package ResultadosGlobales;
use Exporter;
use warnings;
@ISA = ('Exporter');
@EXPORT = ('pedirFiltrosParaConsultaGlobal');


#==============================================================================
sub pedirFiltrosParaConsultaGlobal {
    ($guardarResultado) = @_;
    
    @patronesElegidos = ();
    @sistemasElegidos = ();
    @archivosElegidos = ();
    @ciclosElegidos   = ();
    
    # ---- filtros por sistemas -----------------------------------------------
    print "\e[1;32m=== ELECCION DE FILTRO POR SISTEMAS ===\e[0m\n";
    print "(no escoger ninguno implica no filtrar)\n\n";
    %todosLosSistemas = main::leerArchivoDeSistemas();
    main::mostrarLista(%todosLosSistemas);
    $sistema = 0;
    while ($sistema ne '') {
        $sistema = main::pedirCadena("Ingrese codigo de sistema. Para dejar de ingresar, presione enter:");
        if (exists($todosLosSistemas{$sistema})) {
            push(@sistemasElegidos, $sistema);
        } elsif ($sistema ne '') {
            print "Ingrese un codigo de sistema valido\n";
        }
    }
    
    # ---- filtros por patrones -----------------------------------------------
    print "\n";
    print "\e[1;32m=== ELECCION DE FILTRO POR PATRONES ===\e[0m\n";
    print "(no escoger ninguno implica no filtrar)\n\n";
    %todosLosPatrones = main::leerArchivoDePatrones(@sistemasElegidos);
    main::mostrarLista(%todosLosPatrones);
    $patron = 0;
    while ($patron ne '') {
        $patron = main::pedirNumero("Ingrese numero de patron. Para dejar de ingresar, presione enter:");
        if (exists($todosLosPatrones{$patron})) {
            push(@patronesElegidos, $patron);
        } elsif ($patron ne '') {
            print "Ingrese un numero de patron valido\n";
        }
    }
    
    # ---- filtros por archivos -----------------------------------------------
    print "\n";
    print "\e[1;32m=== ELECCION DE FILTRO POR ARCHIVOS ===\e[0m\n";
    print "(no escoger ninguno implica no filtrar)\n\n";
    $nombreArchivo = 0;    
    while ($nombreArchivo ne '') {
        $nombreArchivo = main::pedirCadena("Ingrese nombre de archivo. Para dejar de ingresar, presione enter:");
        if ($nombreArchivo ne '') {
            push(@archivosElegidos, $nombreArchivo);
        }    
    }

    # ---- filtros por ciclos -------------------------------------------------
    print "\n";
    print "\e[1;32m=== ELECCION DE FILTRO POR CICLOS ===\e[0m\n";
    print "(para indicar rango ingresar xx-yy, con xx inicio del rango e yy fin del mismo)";
    print "(no escoger ninguno implica no filtrar)\n\n";
    $numeroCiclo = 0;
    while ($numeroCiclo ne '') {
        $numeroCiclo = main::pedirCadena("Ingrese numero de ciclo. Para dejar de ingresar, presione enter:");
        if ($numeroCiclo ne '') {
            if ($numeroCiclo =~ /^[0-9]+$/) {
                push(@ciclosElegidos, $numeroCiclo);
            } elsif ($numeroCiclo =~ /^[0-9]+\-[0-9]+$/) {
                @spliteado = split('-', $numeroCiclo);
                @rango = ($spliteado[0] .. $spliteado[1]);
                
                @ciclosElegidos = (@ciclosElegidos, @rango);
            } else {
                print "Ingrese un numero o rango valido\n";
            }
        }
    }
    
    # ---- eleccion de ordenamiento y cantidad de resultados ------------------
    print "\n";
    print "\e[1;32m=== ELECCION DE CRITERIOS DE REPORTE ===\e[0m\n";
    print "(decida si quiere ver los resultados con mayor cantidad o menor cantidad de hallazgos y cuantos)\n\n";
    %opcionesOrdenamiento = ('1' => 'Mayor cantidad de hallazgos', '2' => 'Menor cantidad de hallazgos', '3' => 'Ningun hallazgo');
    main::mostrarLista(%opcionesOrdenamiento);
    $tipoOrdenamiento = 0;
    while (!(exists($opcionesOrdenamiento{$tipoOrdenamiento}))) {
        $tipoOrdenamiento = main::pedirNumero("Ingrese 1, 2 o 3:");
    }
    
    $cantidadDeResultados = 0;
    if ($tipoOrdenamiento == 1 || $tipoOrdenamiento == 2) {
        $cantidadDeResultados = main::pedirNumero("Ingrese el numero maximo de resultados que desea visualizar. El valor por defecto es 5:");
        $cantidadDeResultados = $cantidadDeResultados eq '' ? 5 : $cantidadDeResultados;
    }
    
    # ---- eleccion de criterio para agrupar ----------------------------------
    print "\n";
    print "\e[1;32m=== ELECCION DE CRITERIOS DE AGRUPAMIENTO ===\e[0m\n";
    print "(decida que criterio desea usar para agrupar las cantidades de hallazgos)\n\n";
    %opcionesAgrupamiento = ('1' => 'Por sistema', '2' => 'Por archivo', '3' => 'Por patron', '4' => 'Por archivo y patron', '5' => 'Por archivo y sistema');
    main::mostrarLista(%opcionesAgrupamiento);
    $tipoAgrupamiento = 0;
    while (!(exists($opcionesAgrupamiento{$tipoAgrupamiento}))) {
        $tipoAgrupamiento = main::pedirNumero("Ingrese 1, 2, 3, 4 o 5:");
    }
    
    # ---- invocacion al sub que resuelve la consulta -------------------------
    resolverConsultaGlobal(
        \@sistemasElegidos, 
        \@patronesElegidos, 
        \@archivosElegidos, 
        \@ciclosElegidos, 
        $tipoOrdenamiento,
        $tipoAgrupamiento,
        $cantidadDeResultados, 
        $guardarResultado
    );
}

#==============================================================================
sub resolverConsultaGlobal {
    ($rSistemas, $rPatrones, $rArchivos, $rCiclos, $tipoOrdenamiento, $tipoAgrupamiento, $cantidadDeResultados, $guardarResultado) = @_;
    
    %resultados = generarEsqueletoDeResultados($rSistemas, $rPatrones, $rArchivos, $rCiclos, $tipoAgrupamiento);
    
    if ($tipoAgrupamiento == 1) {
        obtenerHallazgos($rSistemas, $rPatrones, $rArchivos, $rCiclos, \%resultados, "sistema");
    } elsif ($tipoAgrupamiento == 2) {
        obtenerHallazgos($rSistemas, $rPatrones, $rArchivos, $rCiclos, \%resultados, "archivo");
    } elsif ($tipoAgrupamiento == 3) {
        obtenerHallazgos($rSistemas, $rPatrones, $rArchivos, $rCiclos, \%resultados, "patron");
    } elsif ($tipoAgrupamiento == 4) {
        obtenerHallazgos($rSistemas, $rPatrones, $rArchivos, $rCiclos, \%resultados, "patron_y_archivo");
    } elsif ($tipoAgrupamiento == 5) {
        obtenerHallazgos($rSistemas, $rPatrones, $rArchivos, $rCiclos, \%resultados, "sistema_y_archivo");
    }

    if ($tipoOrdenamiento == 1) {
        @clavesResultados = sort { $resultados{$b} <=> $resultados{$a} } (keys(%resultados));
    } elsif ($tipoOrdenamiento == 2) {
        @clavesResultados = sort { $resultados{$a} <=> $resultados{$b} } (keys(%resultados));
    } elsif ($tipoOrdenamiento == 3) {
        @clavesResultados = map { $resultados{$_} == 0 ? $_ : () } keys(%resultados);
    }

    print "\n\n";
    print "\e[1;33m=== RESULTADOS DE LA CONSULTA ===\e[0m\n";
    
    $descripcionFiltros = generarDescripcionDeFiltros($rSistemas, $rPatrones, $rArchivos, $rCiclos);
    main::emitirLineaDeSalida($descripcionFiltros, $guardarResultado);
    $descripcionCriteriosDeReporte = generarDescripcionDeCriterios($tipoAgrupamiento, $tipoOrdenamiento, $cantidadDeResultados);
    main::emitirLineaDeSalida($descripcionCriteriosDeReporte, $guardarResultado);
    main::emitirLineaDeSalida("RESULTADOS:", $guardarResultado);
    
    $mostrados = 0;
    $total = 0;
    foreach (@clavesResultados) {
        $total += $resultados{$_};
        if ($tipoAgrupamiento eq "3") {
            main::emitirLineaDeSalida("Patron $_ tiene " . $resultados{$_}. " hallazgos", $guardarResultado);
        } else {
            main::emitirLineaDeSalida("$_ tiene " . $resultados{$_}. " hallazgos", $guardarResultado);
        }
        $mostrados++;
        if ($mostrados >= $cantidadDeResultados && $cantidadDeResultados > 0) {
            last;
        }
    }
    main::emitirLineaDeSalida("\nEl total de hallazgos es $total", $guardarResultado);
    
    if ($guardarResultado) {
        print "\n\e[1;32mSe ha guardado el reporte de la consulta en " . $main::rutaDeReporte . "\e[0m\n";
    }
}

#==============================================================================
sub obtenerHallazgos {
    ($rSistemas, $rPatrones, $rArchivos, $rCiclos, $rResultados, $criterio) = @_;

    @filtroSistemas = @{$rSistemas};
    @filtroPatrones = @{$rPatrones};
    @filtroArchivos = @{$rArchivos};
    @filtroCiclos   = @{$rCiclos};
    %resultados     = %{$rResultados};

    %hashSistemas = main::leerArchivoDeSistemas();
    @sistemas = keys(%hashSistemas);
    @sistemas = map { $_ ~~ @filtroSistemas || @filtroSistemas == 0 ? $_ : () } @sistemas;
    
    foreach $sistema (@sistemas) {
        %hashPatrones = main::leerArchivoDePatrones(($sistema));
        @patrones = keys(%hashPatrones);
        @patrones = map { $_ ~~ @filtroPatrones || @filtroPatrones == 0 ? $_ : () } @patrones;
        
        foreach $patron (@patrones) {
            $nombreArchivo = main::obtenerRutaDeArchivoProcesadoPorIdDePatron($patron);

            if (!(-f $nombreArchivo)) {
                next;
            }
            open(ARCHIVO, "<$nombreArchivo") or die $!;
            
            while ($linea = <ARCHIVO>) {
                @lineaPartida = split(',', $linea);
                
                if (!($lineaPartida[0] ~~ @filtroCiclos) && @filtroCiclos != 0) {
                    next;
                }
                
                if (!($lineaPartida[1] ~~ @filtroArchivos) && @filtroArchivos != 0) {
                    next;
                }

                if ($criterio eq "sistema") {
                    $resultados{$sistema} += $lineaPartida[2];
                } elsif ($criterio eq "archivo") {
                    $resultados{$lineaPartida[1]} += $lineaPartida[2];
                } elsif ($criterio eq "patron") {
                    $resultados{$patron} += $lineaPartida[2];
                } elsif ($criterio eq "patron_y_archivo") {
                    $resultados{$lineaPartida[1] . " - " . $patron} += $lineaPartida[2];
                } elsif ($criterio eq "sistema_y_archivo") {
                    $resultados{$lineaPartida[1] . " - " . $sistema} += $lineaPartida[2];
                }
            }
            close(ARCHIVO);
        }
    }
    
    return %resultados;
}

#==============================================================================
sub generarEsqueletoDeResultados {
    ($rSistemas, $rPatrones, $rArchivos, $rCiclos, $tipoAgrupamiento) = @_;

    @filtroSistemas = @{$rSistemas};
    @filtroPatrones = @{$rPatrones};
    @filtroArchivos = @{$rArchivos};
    @filtroCiclos   = @{$rCiclos};
    
    %esqueleto = ();
    
    if ($tipoAgrupamiento == 1) {
        %hashSistemas = main::leerArchivoDeSistemas();
        @sistemas = keys(%hashSistemas);
        
        foreach (@sistemas) {
            if ($_ ~~ @filtroSistemas || @filtroSistemas == 0) {
                $esqueleto{$_} = 0;
            }
        }
    } elsif ($tipoAgrupamiento == 2) {
        @archivosProcesados = main::obtenerNombresDeArchivosProcesados(@filtroSistemas);
        foreach (@archivosProcesados) {
            if ($_ ~~ @filtroArchivos || @filtroArchivos == 0) {
                $esqueleto{$_} = 0;
            }
        }
    } elsif ($tipoAgrupamiento == 3) {
        %hashPatrones = main::leerArchivoDePatrones(@filtroSistemas);
        @patrones = keys(%hashPatrones);
        foreach (@patrones) {
            if ($_ ~~ @filtroPatrones || @filtroPatrones == 0) {
                $esqueleto{$_} = 0;
            }
        }
    } elsif ($tipoAgrupamiento == 4) {
        %hashPatrones = main::leerArchivoDePatrones(@filtroSistemas);
        @patrones = keys(%hashPatrones);
        @archivosProcesados = main::obtenerNombresDeArchivosProcesados(@filtroSistemas);
        
        @patrones = map { $_ ~~ @filtroPatrones || @filtroPatrones == 0 ? $_ : () } @patrones;
        @archivosProcesados = map { $_ ~~ @filtroArchivos || @filtroArchivos == 0 ? $_ : () } @archivosProcesados;
        
        @cartesiano = productoCartesiano(\@patrones, \@archivosProcesados);
        foreach (@cartesiano) {
            $esqueleto{@$_[0] . ' - ' . @$_[1]} = 0;
        }
    } elsif ($tipoAgrupamiento == 5) {
        %hashSistemas = main::leerArchivoDeSistemas();
        @sistemas = keys(%hashSistemas);
        @archivosProcesados = main::obtenerNombresDeArchivosProcesados(@filtroSistemas);
        
        @sistemas = map { $_ ~~ @filtroSistemas || @filtroSistemas == 0 ? $_ : () } @sistemas;
        @archivosProcesados = map { $_ ~~ @filtroArchivos || @filtroArchivos == 0 ? $_ : () } @archivosProcesados;
        
        @cartesiano = productoCartesiano(\@sistemas, \@archivosProcesados);
        foreach (@cartesiano) {
            $esqueleto{@$_[0] . ' - ' . @$_[1]} = 0;
        }
    }
    
    return %esqueleto;
}

#==============================================================================
sub generarDescripcionDeCriterios {
    ($tipoAgrupamiento, $tipoOrdenamiento, $cantidadDeResultados) = @_;
    $descripcion = "Criterios de reporte:\n";
    
    if ($tipoOrdenamiento eq '1') {
        $descripcion .= " - Mostrando resultados con mayor cantidad de hallazgos\n";
    } elsif ($tipoOrdenamiento eq '2') {
        $descripcion .= " - Mostrando resultados con menor cantidad de hallazgos\n";
    } elsif ($tipoOrdenamiento eq '3') {
        $descripcion .= " - Mostrando resultados sin hallazgos\n";
    }
    
    if ($tipoOrdenamiento eq '1' || $tipoOrdenamiento eq '2') {
        $descripcion .= " - Mostrando un maximo de $cantidadDeResultados resultados\n";
    }
    
    if ($tipoAgrupamiento eq '1') {
        $descripcion .= " - Agrupando los hallazgos por sistema\n";
    } elsif ($tipoAgrupamiento eq '2') {
        $descripcion .= " - Agrupando los hallazgos por archivo\n";
    } elsif ($tipoAgrupamiento eq '3') {
        $descripcion .= " - Agrupando los hallazgos por patron\n";
    } elsif ($tipoAgrupamiento eq '4') {
        $descripcion .= " - Agrupando los hallazgos por archivo y patron\n";
    } elsif ($tipoAgrupamiento eq '5') {
        $descripcion .= " - Agrupando los hallazgos por archivo y sistema\n";
    }
    
    $descripcion .= "\n";
    return $descripcion;
}

#==============================================================================
sub generarDescripcionDeFiltros {
    ($rSistemas, $rPatrones, $rArchivos, $rCiclos) = @_;
    
    @sistemas = @{$rSistemas};
    @patrones = @{$rPatrones};
    @archivos = @{$rArchivos};
    @ciclos   = @{$rCiclos};
    
    @todosLosPatrones  = main::leerArchivoDePatrones(@sistemas);
    @nombresDePatrones = map { $todosLosPatrones{$_} } @patrones;   
    
    $descripcion  = "Resumen de filtros seleccionados:";
    
    $descripcion .= "\n";
    $descripcion .= " - Sistemas incluidos en la consulta: ";
    $descripcion .= (@sistemas == 0) ? 'todos' : join(', ', @sistemas);
    
    $descripcion .= "\n";
    $descripcion .= " - Patrones incluidos en la consulta: ";
    $descripcion .= (@nombresDePatrones == 0) ? 'todos' : join(', ', @nombresDePatrones);
    
    $descripcion .= "\n";
    $descripcion .= " - Archivos incluidos en la consulta: "; 
    $descripcion .= (@archivos == 0) ? 'todos' : join(', ', @archivos);    
    
    $descripcion .= "\n"; 
    $descripcion .= " - Ciclos incluidos en la consulta: ";
    $descripcion .= (@ciclos == 0) ? 'todos' : join(', ', sort(@ciclos));
    
    $descripcion .= "\n";
    return $descripcion;
}

#==============================================================================
sub productoCartesiano {
    @C = map { [ $_ ] } @{ shift @_ };

    foreach (@_) {
        @A = @$_;
        @C = map { $n = $_; map { [ $n, @$_ ] } @C } @A;
    }

    return @C;
}
